package com.panopset.gui;

import static com.panopset.Util.centerInScreen;
import static com.panopset.Util.getIntegerFromProperty;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.net.URL;
import java.util.Properties;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenuBar;

import com.panopset.AbstractApplication;
import com.panopset.ApplicationInterface;
import com.panopset.centralen.resource.Accessor;

/**
 * Frame that remembers where it was last closed, and its dimensions. Favicon
 * can also be set.
 *
 * @author Karl Dinwiddie
 *
 */
public final class FlexFrame {

    /**
     * Application interface.
     */
    private ApplicationInterface applicationInterface;

    /**
     * Frame attribute key W.
     */
    private static final String W = "W";

    /**
     * Frame attribute key H.
     */
    private static final String H = "H";

    /**
     * Frame attribute key X.
     */
    private static final String X = "X";

    /**
     * Frame attribute key Y.
     */
    private static final String Y = "Y";

    /**
     * Custom frame.
     */
    private JFrame customFrame;

    /**
     * Frame.
     */
    private JFrame frame;

    /**
     * Set visible.
     *
     * @param value
     *            Usually true to show the frame.
     */
    public void setVisible(final boolean value) {
        if (value && applicationInterface != null) {
            Properties properties = applicationInterface.getProperties();
            Integer w = null;
            Integer h = null;
            Integer x = null;
            Integer y = null;
            if (properties != null) {
                w = getIntegerFromProperty(properties, W);
                h = getIntegerFromProperty(properties, H);
                x = getIntegerFromProperty(properties, X);
                y = getIntegerFromProperty(properties, Y);
            }
            Dimension size = null;
            Point location = null;
            if (w != null && h != null && x != null && y != null) {
                size = new Dimension(w, h);
                location = new Point(x, y);
            } else {
                size = LookAndFeel.Dimensions.WINDOW.getValue();
                location = centerInScreen(size);
            }
            getFrame().setSize(size);
            getFrame().setLocation(location);
        }
        getFrame().setVisible(value);
    }

    /**
     * Quit.
     */
    public void quit() {
        if (applicationInterface != null) {
            Properties properties = applicationInterface.getProperties();
            if (properties != null) {
                properties.put(W, "" + getFrame().getSize().width);
                properties.put(H, "" + getFrame().getSize().height);
                properties.put(X, "" + getFrame().getLocation().x);
                properties.put(Y, "" + getFrame().getLocation().y);
            }
            applicationInterface.quit();
        }
        getFrame().dispose();
    }

    /**
     * @return The frame.
     */
    public JFrame getFrame() {
        if (frame == null) {
            if (customFrame == null) {
                frame = new JFrame();
                Dimension size = LookAndFeel.Dimensions.WINDOW.getValue();
                Point location = centerInScreen(size);
                frame.setSize(size);
                frame.setLocation(location);
            } else {
                frame = customFrame;
            }
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.addWindowListener(new WindowListener() {

                public void windowClosing(final WindowEvent e) {
                    quit();
                }

                public void windowActivated(final WindowEvent e) {
                }

                public void windowClosed(final WindowEvent e) {
                }

                public void windowDeactivated(final WindowEvent e) {
                }

                public void windowDeiconified(final WindowEvent e) {
                }

                public void windowIconified(final WindowEvent e) {
                }

                public void windowOpened(final WindowEvent e) {
                }
            });
        }
        return frame;
    }

    /**
     * Set menu bar.
     *
     * @param menuBar
     *            Menu bar.
     */
    public void setMenuBar(final JMenuBar menuBar) {
        getFrame().setJMenuBar(menuBar);
        getFrame().getJMenuBar().invalidate();
        getFrame().validate();
    }

    /**
     * Prevent instantiation outside builder.
     */
    private FlexFrame() { }

    /**
     *
     * FlexFrame builder.
     *
     */
    public static final class Builder {

        /**
         * Application interface.
         */
        private ApplicationInterface bapplicationInterface;

        /**
         * Image icon.
         */
        private ImageIcon bframeIcon;

        /**
         * Menu bar.
         */
        private JMenuBar bmenuBar;

        /**
         * Title.
         */
        private String btitle;

        /**
         * Custom frame.
         */
        private JFrame bcustomFrame;

        /**
         * @return Flex frame.
         */
        public FlexFrame construct() {
            FlexFrame ff = new FlexFrame();
            if (bcustomFrame != null) {
                ff.customFrame = bcustomFrame;
            }
            if (bmenuBar != null) {
                ff.setMenuBar(bmenuBar);
            }
            if (btitle != null) {
                ff.getFrame().setTitle(btitle);
            }
            if (bapplicationInterface == null) {
                if (AbstractApplication.getApp() != null) {
                    bapplicationInterface = AbstractApplication.getApp();
                }
            }
            ff.applicationInterface = bapplicationInterface;
            if (bframeIcon == null) {
                if (AbstractApplication.getApp() != null) {
                    ff.getFrame().setIconImage(
                            AbstractApplication.getApp().getFlexFrame()
                            .getFrame().getIconImage());
                }
            } else {
                ff.getFrame().setIconImage(bframeIcon.getImage());
            }
            return ff;
        }

        /**
         * Provide your own JFrame.
         *
         * @param customFrame
         *            Custom frame.
         * @return Builder
         */
        public Builder customFrame(final JFrame customFrame) {
            bcustomFrame = customFrame;
            return this;
        }

        /**
         * 16x16 icon image from package resource.
         *
         * @param path
         *            Path to image, ie &quot;com/company/icon.png&quot;.
         * @return Builder.
         */
        public Builder packageIcon(final String path) {
            if (path == null) {
                return this;
            }
            return urlIcon(Accessor.getPackageURL(path));
        }

        /**
         * URL must point to a 16x16 icon.
         *
         * @param iconURL
         *            Icon URL.
         * @return Builder.
         */
        public Builder urlIcon(final URL iconURL) {
            if (iconURL == null) {
                return this;
            }
            return frameIcon(new ImageIcon(iconURL));
        }

        /**
         * @param frameIcon
         *            Frame icon must be size 16x16.
         * @return Builder.
         */
        public Builder frameIcon(final ImageIcon frameIcon) {
            bframeIcon = frameIcon;
            return this;
        }

        /**
         *
         * @param menuBar
         *            Menu bar.
         * @return Builder.
         */
        public Builder menuBar(final JMenuBar menuBar) {
            bmenuBar = menuBar;
            return this;
        }

        /**
         * @param title
         *            Title.
         * @return Builder.
         */
        public Builder title(final String title) {
            btitle = title;
            return this;
        }

        /**
         * @param applicationInterface
         *            Application interface.
         * @return Builder.
         */
        public Builder applicationInterface(
                final ApplicationInterface applicationInterface) {
            bapplicationInterface = applicationInterface;
            return this;
        }
    }
}
